/*
 * inotify-sync - a simple file synchronizer and file system watcher
 * Copyright (C) 2010-2011, inotify-sync developers and inotify-sync contributors
 * Copyright (C) 2010-2011, Cohesion Network Security Studio
 *
 * inotify-sync is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * inotify-sync is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with inotify-sync; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <getopt.h>
#include <syslog.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string>

#include "Inotify.h"
#include "EventHandler.h"
#include "ProfileFileParser.h"
#include "Converter.h"

#define PROFILE_DIR "../conf/profile.d"
#define PID_FILE "/run/inotify-sync.pid"

Converter Conv;
std::vector<Profile> profile_vec;

void help();
void get_profile_vec();
void daemonize(Profile &profile);
void start(bool is_daemon);
void stop();
void log_pid();

int main(int argc, char *argv[])
{


//如果无任何参数则打印帮助信息
	if(argc == 1)
	{
		help();
		return 0;
	}

//设置长选项
	struct option longopts[] = 
	{
		{"start", no_argument, NULL, 's'},
		{"stop", no_argument, NULL, 'x'},
		{"run", no_argument, NULL, 'r'},
		{"help", no_argument, NULL, 'h'}
	};
//循环使用getopt_long判断参数
	int ch;
	bool is_start = false;
	bool is_stop = false;
	bool is_run = false;
	while((ch = getopt_long(argc, argv, "sxrh", longopts, NULL))!=-1)
	{
		switch(ch)
		{
		case 's':
			is_start = true;
			break;
		case 'r':
			is_run = true;
			break;
		case 'x':
			is_stop = true;
			break;
		case 'h':
		case '?':
			// 如果有错误的参数，会打印错误信息
			help();
			return 0;
		}
	}
	// 处理选项冲突
	if((is_stop && is_start)
	|| (is_stop && is_run)
	|| (is_start && is_run))
	{
		help();
		return 0;
	}

	// 获取当前有效用户id
	uid_t euid = geteuid();

	if(is_run)
	{
		start(false);
	}
	if(is_start)
	{
		if(euid != 0)
		{
			printf("please run '-s' as root user!\n");
			exit(0);
		}
		start(true);
	}
	if(is_stop)
	{
		if(euid != 0)
		{
			printf("please run '-x' as root user!\n");
			exit(0);
		}
		stop();
	}

	return 0;
}

void help()
{
	printf("Usage: inotify-sync [OPTION...]\n");
	printf("a simple file synchronizer and file system watcher.\n");
	printf("\n");
	printf("Options:\n");
	printf("  -s, --start\t\t\tRun this program on background.\n");
	printf("  -x, --stop\t\t\tStop this program on background.\n");
	printf("  -r, --run\t\t\tRun this program on foreground.\n");
	printf("  -h, --help\t\t\tPrint this message and exit.\n");
}

void get_profile_vec()
{
	ProfileParser *p_profile_parser = new ProfileFileParser(PROFILE_DIR);
	profile_vec = p_profile_parser->get_profiles();
	delete p_profile_parser;
}

void daemonize(Profile &profile)
{
	umask(0);

	struct rlimit rl;
	if(getrlimit(RLIMIT_NOFILE, &rl)<0)
		perror("getrlimit()");

	pid_t pid;
	if((pid = fork()) < 0)
	{
		perror("fork()");
		exit(errno);
	}
	else if(pid != 0)
	{
		// 父进程退出
		exit(0);
	}

	// 子进程继续运行
	log_pid();

	// 关闭所有文件描述符
	if(rl.rlim_max == RLIM_INFINITY)
		rl.rlim_max = 1024;
	for(size_t i = 0;i < rl.rlim_max;i++)
	{
		close(i);
	}

	int fd0, fd1, fd2;
	// 取消标准输入
	fd0 = open("/dev/null", O_RDWR);
	// 重定向标准输出至配置的log文件
	fd1 = open(profile.get_popt("custom_log").c_str(),
			O_WRONLY | O_APPEND | O_CREAT,
			S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	fd2 = open(profile.get_popt("error_log").c_str(),
			O_WRONLY | O_APPEND | O_CREAT,
			S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

	dup2(fd0, STDIN_FILENO);
	dup2(fd1, STDOUT_FILENO);
	dup2(fd2, STDERR_FILENO);

	setsid();
	if(chdir("/") == -1)
	{
		perror("chdir()");
		exit(errno);
	}
}

void start(bool is_daemon)
{
	// 使用get_profile_vec函数设置全局变量profile_vec
	get_profile_vec();
	std::vector<pid_t> pid_vec;
	for(size_t i = 0; i < profile_vec.size(); i++)
	{
		pid_t pid;
		// fork子进程对每个profile进行监控
		if((pid = fork()) < 0)
		{
			perror("fork()");
			exit(errno);
		}
		else if(pid == 0)
		{
			Profile profile = profile_vec[i];

			if(is_daemon)
			{
				daemonize(profile);
			}

			Inotify inotify = Inotify(profile);
			EventHandler event_handler = EventHandler(profile);
			inotify.get_event_handler(&event_handler);
			event_handler.get_inotify(&inotify);
			inotify.monitor();

			exit(0);
		}
		pid_vec.push_back(pid);
	}

	if(is_daemon)
	{
		return;
	}

	int exit_stat = 0;
	while(pid_vec.size())
	{
		std::vector<pid_t>::iterator iter;
		for(iter = pid_vec.begin();iter != pid_vec.end();)
		{
			pid_t pid;
			pid = waitpid(*iter, &exit_stat, WNOHANG);
			if(pid == *iter)
			{
				// 打印进程结束状态
				if(WIFEXITED(exit_stat))
				{
					printf("inotify-sync: process \"%d\" is terminated normally with exit status %d\n",
						pid,
						WEXITSTATUS(exit_stat));
				}
				iter = pid_vec.erase(iter);
			}
			else if(pid == -1)
			{
				perror("waitpid()");
				exit(errno);
			}
			else
			{
				iter++;
			}
		}
	}
}

void stop()
{
	FILE *fp;
	char buf[10] = {'\0'};
	if((fp = fopen(PID_FILE, "r")) == NULL)
	{
		perror("fopen()");
		exit(errno);
	}
	while(fgets(buf, sizeof(buf), fp) != NULL)
	{
		pid_t pid = atoi(buf);
		if(kill(pid, SIGKILL) == -1)
		{
			perror("kill()");
			exit(errno);
		}
	}
	fclose(fp);
	// 删除PID_FILE文件
	if(unlink(PID_FILE) == -1)
	{
		perror("unlink()");
		exit(errno);
	}
}

void log_pid()
{
	int pid_fd;
	// 权限rw-r--r--
	mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
	if((pid_fd = open(PID_FILE, O_WRONLY|O_CREAT, mode)) == -1)
	{
		perror("open()");
		exit(errno);
	}
	FILE *fp;
	if((fp = fdopen(pid_fd, "w")) == NULL)
	{
		perror("fdopen()");
		exit(errno);
	}

	// 获取当前进程pid
	pid_t pid = getpid();
	std::string pid_s = Conv.conv<std::string>(pid);
	int nbytes;
	if((nbytes = fputs(pid_s.c_str(), fp)) == EOF)
	{
		perror("fputs()");
		exit(errno);
	}
	fclose(fp);
	close(pid_fd);
}
